package com.lin.springframework;

import com.lin.app.AppConfig;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class JianlinApplicationContext {
    // 配置类
    private Class configClass;
    // beanDefinitionMap，存储bean定义信息
    private Map<String, BeanDefinition> beanDefinitionMap=new LinkedHashMap<>();
    // 创建单例Bean的缓存 一级缓存 完整bean
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    // 二级缓存 :早期bean
    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
    // 三级缓存 :函数式接口 存放代理对象的bean
    private Map<String, ObjectFactory> factoriesObjects = new ConcurrentHashMap<>();
    // BeanPostProcessor前置/后置处理器列表
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public JianlinApplicationContext(Class<AppConfig> appConfigClass) throws Exception {
        // 读取配置类（配置解析成beandefinition）
        this.configClass = appConfigClass;
        // 解析配置类的扫描包
        scan(configClass);
        // 注册beanPostProcessorq前置/后置处理器
        registerBeanPostProcessors();
        // 一个一个创建单例Bean
        preInstantiateSingletons();
    }

    /**
     * 注册BeanPostProcessor前置/后置处理器
     */
    private void registerBeanPostProcessors() throws Exception {
        // 循环所有的beanDefinitionMap
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            Class beanClass = beanDefinition.getBeanClass();
            // 判断BeanPostProcessor是否被当前类实现（类有没有实现BeanPostProcessor）
            if (BeanPostProcessor.class.isAssignableFrom(beanClass)) {
                // 创建BeanPostProcessor
                BeanPostProcessor bean = (BeanPostProcessor) getBean(beanName);
                beanPostProcessors.add(bean);
            }
            
        }
        
    }

    /**
     * 创建单例Bean
     */
    private void preInstantiateSingletons() throws Exception {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 不是懒加载并且是单例
            if (!beanDefinition.isLazy() && beanDefinition.isSingleton()) {
                getBean(beanName);
            }
        }
    }

    /*private Object getBean(Class clazz) { // 根据类型获取Bean
        // 循环所有的beanDefinitionMap
        // 根据beanDefinition的beanClass和传入的clazz进行对比，如果匹配上了，就创建当前Bean
    }*/

    /**
     * 获取Bean
     * A B
     * @param beanName BeanName
     * @return Bean对象
     */
    public Object getBean(String beanName) throws Exception {
        // 获取单例bean 单重检查锁
        Object bean = getSigeleton(beanName);
        if (bean != null) {
            return bean;
        }
        /**
         * 加锁，保证线程安全，防止多线程环境下重复创建单例bean实例
         * 在多线程环境中，多个线程可能同时进入getBean方法尝试创建同一个单例bean，
         * 使用singletonObjects对象作为锁可以确保同一时间只有一个线程能够执行bean的创建过程，
         * 避免了重复创建和潜在的数据不一致问题
         */
        synchronized (singletonObjects) {

            // 双重检查锁 double-checked locking（A创建的时候依赖b.会把B进行创建，创建完之后，如果线程2进来也有依赖b，这个时候b实例创建完成了，直接返回）
            bean = getSigeleton(beanName);
            if (bean != null) {
                return bean;
            }

            // 如果没有就创建
            // 1.实例化
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            Class beanClass = beanDefinition.getBeanClass();
            Object beanInstance = beanClass.getConstructor().newInstance();

            // 三级缓存，函数式接口调用，因为式二级缓存循环依赖才会走这里
            Object finalBeanInstance = beanInstance;
            factoriesObjects.put(beanName, () -> {
                // 实例化后创建aop动态代理 -- 只有循环依赖的的时候才会走这个逻辑，所以在二级缓存有的话
                for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                    // 如果是SmartInstantiationAwareBeanPostProcessor的实现类
                    if (beanPostProcessor instanceof SmartInstantiationAwareBeanPostProcessor) {
                        return ((SmartInstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(finalBeanInstance, beanName);
                    }
                }
                return finalBeanInstance;
            });


            // 二级缓存放入
            // earlySingletonObjects.put(beanName, beanInstance);


            // 2.属性注入 byName byType Constructor default(@Autowired byType byName)
            // 拿到所有属性去循环
            for (Field declaredField : beanClass.getDeclaredFields()) {
                // 如果这个属性有Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    // 获取属性名（根据#Autowired的byName进行依赖注入）
                    String fieldName = declaredField.getName();
                    // 依赖注入的bean的对象 A.b B.a
                    Object dependBean = getBean(fieldName);
                    // 设置位私有的（因为是private的）
                    declaredField.setAccessible(true);
                    // 给属性赋值(第一个参数：正在创建的实例对象，第二个参数：需要依赖注入的实例对象)
                    declaredField.set(beanInstance, dependBean);
                }
            }

            // 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                beanInstance = beanPostProcessor.postProcessBeforeInitialization(beanInstance, beanName);
            }

            // 3.初始化
            /**
             * 初始化的生命周期回调（三种方式）
             * 1.实现 InitializingBean 接口，重写 afterPropertiesSet() 方法。
             * 2.在 Bean 上声明 @PostConstruct 注解的方法。
             * 3.在 Bean 定义时指定 init-method（如 XML 或注解配置@Bean(initMethod = "方法名") ）。
             */
            if (beanInstance instanceof InitializingBean) {
                // 回调
                ((InitializingBean) beanInstance).afterPropertiesSet();
            }

            // 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                beanInstance = beanPostProcessor.postProcessAfterInitialization(beanInstance, beanName);
            }

            // 4.放入单例缓存池
            singletonObjects.put(beanName, beanInstance);
            // 清除二级缓存池，因为二级缓存只给当前线程使用，临时解决循环依赖
            earlySingletonObjects.remove(beanName);


            return beanInstance;

        }
    }

    /**
     * 从单例缓存池中获取单例Bean
     * @param beanName BeanName
     * @return Bean对象
     */
    private Object getSigeleton(String beanName) {
        // 一级缓存缓存池：完整bean
        if (singletonObjects.containsKey(beanName)) {
            return singletonObjects.get(beanName);
        }
        synchronized (singletonObjects) {
            // 二级缓存缓存池：早期bean
            // 出口   二级缓存有=当前是循环依赖
            if (earlySingletonObjects.containsKey(beanName)) {
                return earlySingletonObjects.get(beanName);
            }
            // 出口   三级级缓存有=当前是循环依赖
            if (factoriesObjects.containsKey(beanName)) {
                ObjectFactory objectFactory = factoriesObjects.get(beanName);
                // 调用函数式接口，创建aop代理对象
                Object aopBean = objectFactory.getObject();
                // 放入二级缓存
                earlySingletonObjects.put(beanName, aopBean);
                // 从三级缓存中移除
                factoriesObjects.remove(beanName);
                return aopBean;
            }
        }

        return null;
    }

    /**
     * 扫描配置类，解析BeanDefinition
     * @param configClass 配置类
     */
    private void scan(Class configClass) throws Exception {

        // 判断配置类上是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            // 获取ComponentScan注解，转换成ComponentScan对象
            ComponentScan componentScan =  (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            // 获取扫描包路径 -> com.lin.app
            String value = componentScan.value();
            // 把路径转换成文件物理路径 com.lin.app -> com/lin/app
            String path = value.replaceAll("\\.", "/");

            // 获取绝对路径
            ClassLoader classLoader = this.getClass().getClassLoader();
            // 目录
            URL resource = classLoader.getResource(path);
            // 获取到目录IO对象 com/lin/app
            File file = new File(resource.getFile());

            List<File> classFiles = new ArrayList<>();

            // 把com/lin/app文件夹下的文件全部放到list中
            if (file.isDirectory()) {
                // 读取所有文件
                for (File f : file.listFiles()) {
                    // 是否还有文件夹（如果有，把文件夹里面的文件也加入到list，一般用递归，因为层级不多就不写了）
                    if (f.isDirectory()) {
                        for (File f1 : f.listFiles()) {
                            classFiles.add(f1);
                        }
                    } else {
                        // 把文件添加到list中
                        classFiles.add(f);
                    }
                }
            }

            // 遍历所有文件classFiles
            for (File classFile : classFiles) {
                //D:\xxx\com\lin\app\service\OrderService.java
                String absolutePath = classFile.getAbsolutePath();
                // com\lin\app\service\OrderService -> com.lin.app.service.OrderService
                String beanClassName = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
                        .replaceAll("\\\\", ".");

                // 获取类对象
                Class<?> beanClass = classLoader.loadClass(beanClassName);

                // 判断类上是否有Component注解,有则表示是一个Bean
                if (beanClass.isAnnotationPresent(Component.class)) {

                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setBeanClass(beanClass);
                    // 是否是懒加载
                    if (beanClass.isAnnotationPresent(Lazy.class)) {
                        beanDefinition.setLazy(true);
                    } else {
                        beanDefinition.setLazy(false);
                    }

                    // 是否是单例，多例
                    if (beanClass.isAnnotationPresent(Scope.class)) {
                        Scope scopeAnnotation  = beanClass.getAnnotation(Scope.class);
                        beanDefinition.setScope(scopeAnnotation.value());
                    } else {
                        beanDefinition.setScope("singleton");
                    }

                    // (在spring还要解析更多的bean定义信息 autowire abstract dependsOn....

                    Component componentAnno = beanClass.getAnnotation(Component.class);
                    String beanName = componentAnno.value();
                    if (beanName.isEmpty()) {
                        // 默认类名首字母小写作为beanName
                        // UserService  ---> userService
                        // OAService  ---> OAService
                        beanName = Introspector.decapitalize(beanClass.getSimpleName());
                    }

                    // key: beanName  value:beanDefinition
                    beanDefinitionMap.put(beanName, beanDefinition);
                }
            }
            System.out.println("beanDefinitionMap = " + beanDefinitionMap);
        }

        // (spring: 解析@Import @Bean) ...
        // (spring: @EnableAspectJAutoPorxy --> 导入AutoProxyCreatorBeanPostProcessor bean的后置处理器，来处理aop)
        // 这里假设，我们除了解析ComponentScan，我们还解析AutoProxyCreatorBeanPostProcessor(创建bean的动态代理后置处理器)
        // 因为我们没有使用@Import，所以这里我们自己写死，手动创建一个beanDefinition
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanClass(AutoProxyCreatorBeanPostProcessor.class);
        // 放到缓存池中
        beanDefinitionMap.put("autoProxyCreatorBeanPostProcessor", beanDefinition);
    }
}
